package com.cupdata.zicon.jdk7concurrent.chapter3;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
learn how to use a semaphore to protect more than one copy of a
resource

The Semaphore object is created
using 3 as the parameter of the constructor. The first three threads that call the acquire()
method will get the access to the critical section of this example, while the rest will be
blocked. When a thread finishes the critical section and releases the semaphore, another
thread will acquire it.
 * 
 * @author SunYabing
 *
 */
public class SemaphoreTest1 {
	public static void main(String[] args) {
		PrintQueue1 printQueue = new PrintQueue1();
		Thread[] thread = new Thread[10];
		for (int i = 0; i < 10; i++) {
			thread[i] = new Thread(new Job1(printQueue), "Thread" + i);
		}

		for (int i = 0; i < 10; i++) {
			thread[i].start();
		}
	}
}

class PrintQueue1 {
	private boolean freePrinters[];
	private Lock lockPrinters;
	private final Semaphore semaphore;

	public PrintQueue1() {
		semaphore = new Semaphore(3);//
		freePrinters = new boolean[3];
		for (int i = 0; i < 3; i++) {
			freePrinters[i] = true;
		}
		lockPrinters = new ReentrantLock();
	}

	public void printJob(Object document) {
		try {
			semaphore.acquire();
			int assignedPrinter = getPrinter();
			long duration = (long) (Math.random() * 10);
			System.out.printf(
					"%s: PrintQueue: Printing a Job during %d seconds\n",
					Thread.currentThread().getName(), duration);
			TimeUnit.SECONDS.sleep(duration);
			freePrinters[assignedPrinter] = true;
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			semaphore.release();
		}
	}

	private int getPrinter() {
		int ret = -1;
		try {
			lockPrinters.lock();
			for (int i = 0; i < freePrinters.length; i++) {
				if (freePrinters[i]) {
					ret = i;
					freePrinters[i] = false;
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lockPrinters.unlock();
		}
		return ret;
	}

}

class Job1 implements Runnable {
	private PrintQueue1 printQueue;

	public Job1(PrintQueue1 printQueue) {
		this.printQueue = printQueue;
	}

	@Override
	public void run() {
		System.out.printf("%s: Going to print a job\n", Thread.currentThread()
				.getName());
		printQueue.printJob(new Object());
		System.out.printf("%s: The document has been printed\n", Thread
				.currentThread().getName());
	}
}
